﻿using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Web.Mvc;
using System.Web.Mvc.Html;
using System.Web.Mvc.Ajax;
using System.Web.UI.WebControls;
using System.Web.Routing;

namespace ChoViet.Lib.Helpers
{
    public static class TreeViewHtmlHelper
    {
        #region Attribute Members

        private static string strEndCode = "|--";

        #endregion

        #region TreeView Members
        /// <summary>
        /// Create a TreeView of nodes starting from a root element
        /// </summary>
        /// <param name="treeId">The ID that will be used when the ul is created</param>
        /// <param name="rootItems">The root nodes to create</param>
        /// <param name="childrenProperty">A lambda expression that returns the children nodes</param>
        /// <param name="itemContent">A lambda expression defining the content in each tree node</param>
        public static string TreeView<T>(this HtmlHelper html, string treeId, IEnumerable<T> rootItems, Func<T, IEnumerable<T>> childrenProperty, Func<T, string> itemContent)
        {
            return html.TreeView(treeId, rootItems, childrenProperty, itemContent, true, null);
        }

        /// <summary>
        /// Create a TreeView of nodes starting from a root element
        /// </summary>
        /// <param name="treeId">The ID that will be used when the ul is created</param>
        /// <param name="rootItems">The root nodes to create</param>
        /// <param name="childrenProperty">A lambda expression that returns the children nodes</param>
        /// <param name="itemContent">A lambda expression defining the content in each tree node</param>
        /// <param name="includeJavaScript">If true, output will automatically render the JavaScript to turn the ul into the treeview</param>    
        public static string TreeView<T>(this HtmlHelper html, string treeId, IEnumerable<T> rootItems, Func<T, IEnumerable<T>> childrenProperty, Func<T, string> itemContent, bool includeJavaScript)
        {
            return html.TreeView(treeId, rootItems, childrenProperty, itemContent, includeJavaScript, null);
        }

        /// <summary>
        /// Create a TreeView of nodes starting from a root element
        /// </summary>
        /// <param name="treeId">The ID that will be used when the ul is created</param>
        /// <param name="rootItems">The root nodes to create</param>
        /// <param name="childrenProperty">A lambda expression that returns the children nodes</param>
        /// <param name="itemContent">A lambda expression defining the content in each tree node</param>
        /// <param name="includeJavaScript">If true, output will automatically render the JavaScript to turn the ul into the treeview</param>
        /// <param name="emptyContent">Content to be rendered when the tree is empty</param>
        /// <param name="includeJavaScript">If true, output will automatically into the JavaScript to turn the ul into the treeview</param>    
        public static string TreeView<T>(this HtmlHelper html, string treeId, IEnumerable<T> rootItems, Func<T, IEnumerable<T>> childrenProperty, Func<T, string> itemContent, bool includeJavaScript, string emptyContent)
        {
            StringBuilder sb = new StringBuilder();

            sb.AppendFormat("<ul id='{0}'>\r\n", treeId);

            if (rootItems.Count() == 0)
            {
                sb.AppendFormat("<li>{0}</li>", emptyContent);
            }

            foreach (T item in rootItems)
            {
                RenderLi(sb, item, itemContent);
                AppendChildren(sb, item, childrenProperty, itemContent);
            }

            sb.AppendLine("</ul>");

            if (includeJavaScript)
            {
                sb.AppendFormat(
                    @"<script type='text/javascript'>
                    $(document).ready(function() {{
                        $('#{0}').treeview({{ animated: 'fast' }});
                    }});
                </script>", treeId);
            }

            return sb.ToString();
        }

        private static void AppendChildren<T>(StringBuilder sb, T root, Func<T, IEnumerable<T>> childrenProperty, Func<T, string> itemContent)
        {
            var children = childrenProperty(root);
            if (children.Count() == 0)
            {
                sb.AppendLine("</li>");
                return;
            }

            sb.AppendLine("\r\n<ul>");
            foreach (T item in children)
            {
                RenderLi(sb, item, itemContent);
                AppendChildren(sb, item, childrenProperty, itemContent);
            }

            sb.AppendLine("</ul></li>");
        }

        private static void RenderLi<T>(StringBuilder sb, T item, Func<T, string> itemContent)
        {
            sb.AppendFormat("<li>{0}", itemContent(item));
        }
        #endregion

        #region TreeViewList Members

        public static string TreeViewList(this System.Web.Mvc.HtmlHelper htmlHelper, string name)
        {
            return TreeViewList(htmlHelper, name, null /* selectList */, null /* optionLabel */, null /* htmlAttributes */);
        }

        public static string TreeViewList(this System.Web.Mvc.HtmlHelper htmlHelper, string name, string optionLabel)
        {
            return TreeViewList(htmlHelper, name, null /* selectList */, optionLabel, null /* htmlAttributes */);
        }

        public static string TreeViewList(this System.Web.Mvc.HtmlHelper htmlHelper, string name, TreeNodeCollection tree)
        {
            return TreeViewList(htmlHelper, name, tree, null /* optionLabel */, null /* htmlAttributes */);
        }

        public static string TreeViewList(this System.Web.Mvc.HtmlHelper htmlHelper, string name, TreeNodeCollection tree, object htmlAttributes)
        {
            return TreeViewList(htmlHelper, name, tree, null /* optionLabel */, new RouteValueDictionary(htmlAttributes));
        }

        public static string TreeViewList(this System.Web.Mvc.HtmlHelper htmlHelper, string name, TreeNodeCollection tree, IDictionary<string, object> htmlAttributes)
        {
            return TreeViewList(htmlHelper, name, tree, null /* optionLabel */, htmlAttributes);
        }

        public static string TreeViewList(this System.Web.Mvc.HtmlHelper htmlHelper, string name, TreeNodeCollection tree, string optionLabel)
        {
            return TreeViewList(htmlHelper, name, tree, optionLabel, null /* htmlAttributes */);
        }

        public static string TreeViewList(this System.Web.Mvc.HtmlHelper htmlHelper, string name, TreeNodeCollection tree, string optionLabel, object htmlAttributes)
        {
            return TreeViewList(htmlHelper, name, tree, optionLabel, new RouteValueDictionary(htmlAttributes));
        }

        public static string TreeViewList(this HtmlHelper htmlHelper, string name, TreeNodeCollection tree, string optionLabel, IDictionary<string, object> htmlAttributes)
        {
            return htmlHelper.DropDownList(name, CreateSelectList(tree), optionLabel, htmlAttributes).ToString();
        }

        private static IEnumerable<SelectListItem> CreateSelectList(TreeNodeCollection tree)
        {
            if (tree == null || tree.Count <= 0)
                return CreateEmptySelectList();

            List<SelectListItem> retL = new List<SelectListItem>();
            for (var i = 0; i < tree.Count; i++)
            {
                retL.Add(CreateSelectListItemFromTheNode(tree[i]));
                if (tree[i].ChildNodes != null && tree[i].ChildNodes.Count > 0)
                {
                    // Call recursive
                    retL.AddRange(CreateSelectList(tree[i].ChildNodes));
                }
            }

            return retL;
        }

        private static SelectList CreateEmptySelectList()
        {
            return new SelectList(new List<SelectListItem>());
        }

        private static SelectListItem CreateSelectListItemFromTheNode(TreeNode node)
        {
            if (node == null)
                return CreateEmptySelectListItem();

            StringBuilder sbPrefix = new StringBuilder();
            for (var i = 0; i < node.Depth; i++)
            {
                sbPrefix.Append(strEndCode);
            }
            sbPrefix.Append(node.Text);

            return new SelectListItem
            {
                Text = sbPrefix.ToString(),
                Value = node.Value,
                Selected = node.Selected
            };
        }

        private static SelectListItem CreateEmptySelectListItem()
        {
            return new SelectListItem();
        }

        #endregion
    }
}
